import { _decorator, Camera, Color, Component, EventTouch, geometry, Input, input, instantiate, Material, ModelComponent, Node, NodePool, PhysicsSystem, Prefab, primitives, Rect, Texture2D, utils, Vec2, Vec3 } from 'cc';
import { Ele } from './Ele';
import GateCfg from './data/GateCfg';
import { ZhuZICtr } from './ZhuZICtr';
import EventCenter from './EventCenter';
import PreData from './data/PreData';
import SoundManager from './SoundManager';
import ViewType from './ViewType';
const { ccclass, property } = _decorator;

@ccclass('Game')
export class Game extends Component {
    @property(Prefab)
    private mzhuzi:Prefab=null;
    @property(Prefab)
    private mElePrefab:Prefab=null;
    @property(Node)
    private nodeParent:Node=null;
    @property(Material)
    private mZhuZiMat:Material=null;
    @property([Texture2D])
    private mEleSkins:Array<Texture2D>=[];

    private mZhuZiPool:NodePool=new NodePool();
    private mElePool:NodePool=new NodePool();
    private mRads:number=0.3;
    private mTube:number=.16;
    private mOpts:any={}
    @property(Camera)
    private mCamera:Camera=null;
    private ray = new geometry.Ray();
    private mGameCfg:GateCfg;
    private mZhuZis:Array<Node>=[];
    private mIsGameing:boolean=false;
   
    private mStepDatas:Array<PreData>=[];
    private mRectInfo={};
    protected onEnable(): void {
        input.on(Input.EventType.TOUCH_START,this.touchStart,this);
        
    }
    protected onDisable(): void {
        input.off(Input.EventType.TOUCH_START,this.touchStart,this);
    }

    private touchStart(e){
        if(!this.mIsGameing)return;
        
        const result = this.getTouchModel(e);
        console.log("ssssssssssssssssssss touch",result?true:false);
        if(result) {
            // SoundManager.ins.playOneShot("click");
            let currzhuziCtr:ZhuZICtr = result.node.getComponent(ZhuZICtr);
            if(currzhuziCtr.isComplete(false))return;
            let tselectzhuzi = this.getSelectZhuZi();
            if(tselectzhuzi){
                let tctr:ZhuZICtr = tselectzhuzi.getComponent(ZhuZICtr);
                if(currzhuziCtr == tctr){
                    tctr.setSelect(false);
                    return;
                }
                if(!currzhuziCtr.canFly(tctr)){
                    tctr.setSelect(false);
                    currzhuziCtr.setSelect(true);
                    return;
                }
                let tflys = tctr.getSelectFlyNodes(currzhuziCtr.getEmptyPosNum());
                if(tflys.length){
                    this.mIsGameing=false;
                    tctr.setSelect(false);
                    let tself = this;
                    currzhuziCtr.addFlyEles(tflys,()=>{
                        tself.mIsGameing=true;
                        let tchangeInfo=tctr.getChangeIdInfo();
                        if(tchangeInfo.num>0){
                            tself.changeZhuZiEleIds(tctr,tchangeInfo);
                        }
                        if(tself.checkGameComplete())tself.gameComplete();
                        tself.mStepDatas.push(new PreData(tctr,currzhuziCtr,tflys.length));
                        if(currzhuziCtr.isComplete(false)){
                            // let tdata={v1:currzhuziCtr.getWorldPos(),v2:currzhuziCtr.getWorldTopPos()};
                            // EventCenter.getIns().emit(ViewType.show_anim_by_pos,[tdata]);
                            currzhuziCtr.playCompleteEff();
                        }
                    });
                }
            }else{
                currzhuziCtr.setSelect(true);
            }
        }else{
            let tselectzhuzi = this.getSelectZhuZi();
            if(tselectzhuzi)tselectzhuzi.getComponent(ZhuZICtr).setSelect(false);
        }
    }

    private  getTouchModel(e:EventTouch) {
        const location = e.getLocation();
        // for(let key in this.mRectInfo){
        //     if(this.mRectInfo[key].rect.contains(location))return this.mRectInfo[key].collider;
        // }
        let titem=null;
        this.mCamera.screenPointToRay(location.x, location.y, this.ray);
        if (PhysicsSystem.instance.raycastClosest(this.ray)) {
            let tres = PhysicsSystem.instance.raycastClosestResult;
            if(tres)titem=tres.collider;
        }
        // if(titem){
        //     let tctr:ZhuZICtr = titem.node.getComponent(ZhuZICtr);
        //     let tv1 = tctr.getWorldPos();
        //     let tnode = this.node.parent.getChildByName("Canvas");
        //     let tp1 = this.getScenePos(tv1,tnode);
        //     let tp2 = this.getScenePos(tctr.getWorldTopPos(),tnode);
        //     let tv2 = tv1.clone();
        //     tv2.x =+ 0.5;
        //     let tp3 = this.getScenePos(tv2,tnode);
        //     let trc:Rect=new Rect(location.x,location.y,Math.abs(tp3.x-tp1.x)*2,Math.abs(tp2.y-tp1.y));
        //     this.mRectInfo[titem.node.name]={rect:trc,collider:titem};
        //     trc.contains(location);
        // }
        return titem;
    }

    public setData(cfg,isSp:boolean=false){
        this.clearAll();
        if(cfg){
            this.mStepDatas=[];
            this.mGameCfg=new GateCfg(cfg,isSp);
            this.initZhuzi();
            
        }
    }

    public fanhui(func:Function){
        if(!this.mIsGameing)return;
        if(!this.mStepDatas.length){
            func(false);
            this.mIsGameing=true;
            return;
        }
        let tstep = this.mStepDatas.pop();
        let tflys = tstep.to.getSelectFlyNodes(tstep.num,true);
        this.mIsGameing=false;
        tstep.from.addFlyEles(tflys,()=>{
            this.mIsGameing=true;
            func(true);
        });
    }

    public grow(func:Function){
        if(!this.mIsGameing)return;
        let tctr:ZhuZICtr=this.mZhuZis[this.mZhuZis.length-1].getComponent(ZhuZICtr);
        if(tctr.isUnlockAll()){
            func(false);
            this.mIsGameing=true;
            return;
        }
        tctr.unlockOne();
        this.scheduleOnce(()=>{
            this.mIsGameing=true;
            func(true);
        },0.05);
        
    }

    public restAllEles(){
        if(!this.mIsGameing)return;
        this.mIsGameing=false;
        let tallEles=[];
        for(let i=0;i<this.mZhuZis.length;i++){
            let teles = this.mZhuZis[i].getComponent(ZhuZICtr).removeAllEles();
            tallEles = tallEles.concat(teles);
        }
        this.scheduleOnce(()=>{
            for(let i=0;i<this.mZhuZis.length;i++){
                this.mZhuZis[i].getComponent(ZhuZICtr).addEles(this.getEleByCfg(tallEles,this.mGameCfg.zhuzi[i]));
            }
        },1);
        this.scheduleOnce(()=>{this.mIsGameing=true},1.5);
    }
    
    public breakAllEles(){
        if(!this.mIsGameing)return;
        this.mIsGameing=false;
        let tallEles=[];
        let tnumData=[];
        for(let i=0;i<this.mZhuZis.length;i++){
            let teles = this.mZhuZis[i].getComponent(ZhuZICtr).removeAllEles();
            tnumData[i] = teles.length;
            tallEles = tallEles.concat(teles);
        }
        this.scheduleOnce(()=>{
            for(let i=0;i<this.mZhuZis.length;i++){
                this.mZhuZis[i].getComponent(ZhuZICtr).addEles(this.getRandomFromArr(tallEles,tnumData[i]));
            }
        },1);
        
    }

    private changeZhuZiEleIds(ctr:ZhuZICtr,data){
        ctr.changeSkin(this.getEleSkin(data.id-1),data.id);
    }

    private startGame(){
        this.scheduleOnce(()=>{this.mIsGameing=true;},1.5)
        
    }

    private gameComplete(){
        this.mIsGameing=false;
       
        EventCenter.getIns().emit(ViewType.game_complete);
    }

    private checkGameComplete(){
        for(let i=0;i<this.mZhuZis.length;i++){
            if(!this.mZhuZis[i].getComponent(ZhuZICtr).isComplete())return false;
        }
        return true;
    }

    private initZhuzi(){
        this.mZhuZis=[];
        let tallUnlock;
        let tzhuziNum = this.mGameCfg.zhuzi.length;
        for(let i=0;i<tzhuziNum;i++){
            let tzhuzi = this.createZhuZi();
            tzhuzi.name="zhu_"+i;
            this.addEles(tzhuzi,this.mGameCfg.zhuzi[i]);
            this.mZhuZis.push(tzhuzi);
            tallUnlock=true;
            if(i==tzhuziNum-1 && tzhuziNum>2){
                tallUnlock=false;
            }
            tzhuzi.parent=this.nodeParent;
            tzhuzi.getComponent(ZhuZICtr).setLen(this.mGameCfg.len,tallUnlock);
        }
        this.freshZhuZiPos();
        this.startGame();
        this.mRectInfo={};
    }

    private getSelectZhuZi(){
        for(let i=0;i<this.mZhuZis.length;i++){
            if(this.mZhuZis[i].getComponent(ZhuZICtr).getIsSelect())return this.mZhuZis[i];
        }
        return null;
    }

    private freshZhuZiPos(){
        let tdist = 1.1;
        let tdistZ=4;
        let tposinfo = this.getPosInfoByLen(this.mZhuZis.length);
        let tnum1 = tposinfo[0];
        let tnum2=tposinfo[1];
        let tnum3=tposinfo[2];
        let tstart1 = -(tnum1*tdist)/2+tdist/2;
        let tstart2 = -(tnum2*tdist)/2+tdist/2;
        let tstart3 = -(tnum3*tdist)/2+tdist/2;
        let tstartZ:number = 0;
        let tstep = tnum2>0?1:0;
        if(tnum3>0){
            tstep=2;
        }
        tstartZ = -(tstep*tdistZ)/2;
        for(let i=0;i<this.mZhuZis.length;i++){
            if(i<tnum1){
                this.mZhuZis[i].setPosition(tstart1+tdist*i,0,tstartZ);
            }else if(i<tnum1+tnum2){
                this.mZhuZis[i].setPosition(tstart2+tdist*(i%tnum1),0,tstartZ+tdistZ);
            }
            else{
                this.mZhuZis[i].setPosition(tstart3+tdist*(i%(tnum1+tnum2)),0,tstartZ+tdistZ*2);
            }
        }
    }

    private addEles(node:Node,cfg){
        let teles=[];
        for(let i=0;i<cfg.eles.length;i++){
            teles.push(this.createEle(cfg.eles[i]));
        }
        this.scheduleOnce(()=>{
            node.getComponent(ZhuZICtr).addEles(teles);
        },0.1);
    }

    private createEle(data=null){
        let tnode:Node=this.mElePool.get();
        if(!tnode){
            tnode=instantiate(this.mElePrefab)//new Node();
            // let tmodel = tnode.addComponent(ModelComponent);
            // let ttorus=primitives.torus(this.mRads,this.mTube,this.mOpts);
            // let torusMesh = utils.MeshUtils.createMesh(ttorus);
            // tmodel.mesh = torusMesh;
            //let tctr = tnode.addComponent(Ele);
            //tctr.addMat(this.mZhuZiMat);
        }
        if(data){
            tnode.getComponent(Ele).setData(data);
            //tnode.getComponent(Ele).setEleColor(this.getColor(data-1));
            tnode.getComponent(Ele).setSkinTexture(this.getEleSkin(data-1));
        }
        return tnode;
    }

    private recoveEle(node:Node){
        node.removeFromParent();
        this.mElePool.put(node);
    }

    private recoveEles(eles){
        for(let i=0;i<eles.length;i++){
            this.recoveEle(eles[i]);
        }
    }

    private createZhuZi():Node{
        let tnode = this.mZhuZiPool.get();
        if(!tnode){
            tnode=instantiate(this.mzhuzi);
        }
        return tnode;
    }

    private recoveZhuZi(node:Node){
        node.removeFromParent();
        this.mZhuZiPool.put(node);
    }


    private clearZhuZi(){
        let teles
        for(let i=0;i<this.mZhuZis.length;i++){
            teles = this.mZhuZis[i].getComponent(ZhuZICtr).clearEles();
            this.recoveEles(teles);
            this.recoveZhuZi(this.mZhuZis[i]);
        }
        teles=null;
        this.mZhuZis=[];
    }

    public clearAll(){
        this.mStepDatas=[];
        this.clearZhuZi();
    }

    private getRandomFromArr(arr,num:number){
        let tarr = arr;
        let teles=[];
        for(let i=0;i<num;i++){
            let tindex = Math.floor(Math.random()*tarr.length);
            teles.push(tarr.splice(tindex,1)[0]);
        }
        return teles;
    }

    private getEleByCfg(arr,cfg){
        let tarr=[];
        let teles = cfg.eles;
        let tele;
        for(let i=0;i<teles.length;i++){
            tele=arr.pop();
            if(tele){
                tele.getComponent(Ele).setSkinTextureAndId(this.getEleSkin(teles[i]-1),teles[i]);
                tarr.push(tele);
            }
        }
        return tarr;
    }

    private findEleByCfg(arr,cfg){
        let tarr=[];
        let teles = cfg.eles;
        let tele;
        for(let i=0;i<teles.length;i++){
            tele=this.findEleInArrByIndex(arr,teles[i]);
            if(tele)tarr.push(tele);
        }
        return tarr;
    }

    private findEleInArrByIndex(arr,indiex){
        for(let i=0;i<arr.length;i++){
            if(arr[i].getComponent(Ele).data==indiex)return arr.splice(i,1)[0];
        }
        return null;
    }
   
    private mColors=[new Color(255, 0, 0),new Color(0, 255, 0),new Color(0, 0, 255),new Color(255, 255, 0),new Color(0, 255, 255)];
    private getColor(index:number){
        let tc=this.mColors[index];
        return tc;
    }

    private getEleSkin(index:number){
        let tindex = index;
        if(index>=10)tindex=9;
        return this.mEleSkins[tindex];
    }

    private getPosInfoByLen(len:number){
        let tnum1=0;
        let tnum2=0;
        let tnum3=0;
        if(len>=9){
            tnum1=Math.ceil(len/3);
            let temp = len-tnum1;
            tnum2 = Math.ceil(temp/2);
            tnum3=temp=tnum2;
        }else if(len==2){
            tnum1=len;
        }else{
            tnum1=Math.ceil(len/2);
            tnum2=len-tnum1;
        }
        return [tnum1,tnum2,tnum3]
    }

    public getScenePos(vec3:Vec3,node:Node){
        return this.mCamera.convertToUINode(vec3,node);
    }

    public get isGameing(){
        return this.mIsGameing;
    }

    public get data():GateCfg{
        return this.mGameCfg;
    }

}


